const std = @import("std");

// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
    // Standard target options allows the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults, which
    // means any target is allowed, and the default is native. Other options
    // for restricting supported target set are available.
    @import("zlbmath").zlbmath_print();
    const target = b.standardTargetOptions(.{});

    // Standard optimization options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
    // set a preferred release mode, allowing the user to decide how to optimize.
    const optimize = b.standardOptimizeOption(.{});

    // This creates a "module", which represents a collection of source files alongside
    // some compilation options, such as optimization mode and linked system libraries.
    // Every executable or library we compile will be based on one or more modules.
    const lib_mod = b.addModule("zlibcell", .{
        // `root_source_file` is the Zig "entry point" of the module. If a module
        // only contains e.g. external object files, you can make this `null`.
        // In this case the main source file is merely a path, however, in more
        // complicated build scripts, this could be a generated file.
        .root_source_file = b.path("src/root.zig"),
        .target = target,
        .optimize = optimize,
    });

    // We will also create a module for our other entry point, 'main.zig'.
    const exe_mod = b.createModule(.{
        // `root_source_file` is the Zig "entry point" of the module. If a module
        // only contains e.g. external object files, you can make this `null`.
        // In this case the main source file is merely a path, however, in more
        // complicated build scripts, this could be a generated file.
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    const rbtree_deps = b.dependency("rbtree", .{
        .target = target,
        .optimize = optimize,
    });
    const zlbmath_deps = b.dependency("zlbmath", .{
        .target = target,
        .optimize = optimize,
    });
    lib_mod.addImport("zlbmath", zlbmath_deps.module("zlbmath"));

    // Modules can depend on one another using the `std.Build.Module.addImport` function.
    // This is what allows Zig source code to use `@import("foo")` where 'foo' is not a
    // file path. In this case, we set up `exe_mod` to import `lib_mod`.
    exe_mod.addImport("zlibcell", lib_mod);
    exe_mod.addImport("rbtree", rbtree_deps.module("rbtree"));
    lib_mod.addImport("rbtree", rbtree_deps.module("rbtree"));

    // Now, we will create a static library based on the module we created above.
    // This creates a `std.Build.Step.Compile`, which is the build step responsible
    // for actually invoking the compiler.
    const lib = b.addLibrary(.{
        .linkage = .static,
        .name = "zlibcell",
        .root_module = lib_mod,
    });

    // This declares intent for the library to be installed into the standard
    // location when the user invokes the "install" step (the default step when
    // running `zig build`).
    b.installArtifact(lib);
    if (target.result.os.tag == .emscripten) {
        return;
    }
    // This creates another `std.Build.Step.Compile`, but this one builds an executable
    // rather than a static library.
    const exe = b.addExecutable(.{
        .name = "zlibcell",
        .root_module = exe_mod,
    });
    exe.linkLibC();
    // exe.root_module.
    // This declares intent for the executable to be installed into the
    // standard location when the user invokes the "install" step (the default
    // step when running `zig build`).
    b.installArtifact(exe);

    // This *creates* a Run step in the build graph, to be executed when another
    // step is evaluated that depends on it. The next line below will establish
    // such a dependency.
    const run_cmd = b.addRunArtifact(exe);

    // By making the run step depend on the install step, it will be run from the
    // installation directory rather than directly from within the cache directory.
    // This is not necessary, however, if the application depends on other installed
    // files, this ensures they will be present and in the expected location.
    run_cmd.step.dependOn(b.getInstallStep());

    // This allows the user to pass arguments to the application in the build
    // command itself, like this: `zig build run -- arg1 arg2 etc`
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    // This creates a build step. It will be visible in the `zig build --help` menu,
    // and can be selected like this: `zig build run`
    // This will evaluate the `run` step rather than the default, which is "install".
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    // Creates a step for unit testing. This only builds the test executable
    // but does not run it.
    const lib_unit_tests = b.addTest(.{
        .root_module = lib_mod,
    });

    const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

    const exe_unit_tests = b.addTest(.{
        .root_module = exe_mod,
    });
    // exe_unit_tests.root_module.addImport("zlibcell", lib_mod);
    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);

    // Similar to creating the run step earlier, this exposes a `test` step to
    // the `zig build --help` menu, providing a way for the user to request
    // running the unit tests.
    const test_step = b.step("test", "Run unit tests");
    const search_dir = [_][]const u8{"src"};

    for (search_dir) |sdir| {
        const abs_sdir: []const u8 = b.build_root.join(b.allocator, &[_][]const u8{sdir}) catch continue;
        defer b.allocator.free(abs_sdir);
        var open_sdir = std.fs.openDirAbsolute(abs_sdir, .{ .iterate = true }) catch continue;
        defer open_sdir.close();

        var walker = open_sdir.walk(b.allocator) catch continue;
        defer walker.deinit();
        while (walker.next() catch null) |entry| {
            const full_path = b.pathJoin(&.{ sdir, entry.path });
            defer b.allocator.free(full_path);

            if (!std.mem.endsWith(u8, full_path, ".zig")) continue;
            // if (std.mem.eql(u8, full_path, "src/main.zig")) continue;
            if (std.mem.eql(u8, full_path, "src/algorithm/tools/mesh/mesh.zig")) {
                std.debug.print("***************\n", .{});
                const one_mod = b.createModule(.{
                    .root_source_file = b.path(full_path),
                    .target = target,
                    .optimize = optimize,
                });
                std.debug.print("full path zlibcell:{s}\n", .{full_path});
                one_mod.addImport("rbtree", rbtree_deps.module("rbtree"));
                one_mod.addImport("zlbmath", zlbmath_deps.module("zlbmath"));
            }

            const one_test = b.addTest(.{
                .root_module = b.createModule(.{
                    .root_source_file = b.path(full_path),
                    .target = target,
                    .optimize = optimize,
                }),
                // .root_source_file = b.path(full_path),
                // .target = target,
                // .optimize = optimize,
            });
            // const one_mod = b.createModule(.{
            //     .root_source_file = b.path(full_path),
            //     .target = target,
            //     .optimize = optimize,
            // });
            // std.debug.print("full path zlibcell:{s}\n", .{full_path});
            // one_mod.addImport("rbtree", rbtree_deps.module("rbtree"));
            // one_mod.addImport("zlbmath", zlbmath_deps.module("zlbmath"));
            one_test.root_module.addImport("zlibcell", lib_mod);
            one_test.root_module.addImport("rbtree", rbtree_deps.module("rbtree"));
            one_test.root_module.addImport("zlbmath", zlbmath_deps.module("zlbmath"));

            const one_test_run = b.addRunArtifact(one_test);
            test_step.dependOn(&one_test_run.step);
        }
    }

    test_step.dependOn(&run_lib_unit_tests.step);
    test_step.dependOn(&run_exe_unit_tests.step);
}
